package org.tigr.htc.common;

/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.Properties;

import javax.swing.event.EventListenerList;

import org.apache.log4j.Logger;

/**
 * The class <b>HTCConfig</b> is used to help get access to the
 * configuration properties.
 */
public class HTCConfig {

    /** The field <code>log</code> */
    static Logger log = Logger.getLogger(HTCConfig.class);

    /** The field <code>conf</code> */
    static private Properties conf = null;

    /** The field <code>configLocation</code> */
    private static final String appConfigName = "htcservice.conf";
    private static final String peerListName = "peers.list";
    
    private static String configLocation;
    
    /**
     * <code>list</code> holds the list of listeners.
     */
    static protected EventListenerList list = new EventListenerList();

    /**
     * The constructor <code>HTCConfig</code> creates an instance of the class
     *
     */
    private HTCConfig() {}

    /* In previous versions, we relied on the existence of a system property being set to
    point to the correct location of the application configuration file. This causes problems
	when deploying to a servlet container like tomcat because you have to ensure that the
	servlet container is started with those properties being specified. Here we now rely on
	the class loading mechanism to get to our configuration file. When the application is
	finally deployed to the servlet container, there should be no additional settings
	required.
	   
	Note: There are almost certainly other areas in the code that load properties files
	by relying on absolute paths of filename from the filesystem. We will have to hunt
	these down one by one to make deployment less problematic. The current build.xml is
	still responsible for modifying these settings in the main configuration file at
	deploy time. */
    
    /**
     * The method <code>initConfig<code>
     *
     * @throws RuntimeException
     */
    public synchronized static void initConfig() throws RuntimeException {

        if (conf == null) {
            if (System.getProperty("org.tigr.htc.config") != null) {
                // The user has specified a custom HTC config file so use that
                configLocation = System.getProperty("org.tigr.htc.config");
            }
            else {
                // Use the standard config file 
                try {
                    URL configURL = HTCConfig.class.getClassLoader().getResource("conf/" + appConfigName);

                    if (configURL == null) {
                        String msg = "Configuration file not found.";
                        log.error(msg);
                        throw new FileNotFoundException(msg);
                    }

                    configLocation = configURL.getPath();
                } catch(IOException e) {
                   throw new RuntimeException("Configuration file problem with " + appConfigName + ". Reason: " +
                                               e.getMessage());
               }
            }
            
            // Load the properties from the file
            try {
                InputStream stream = new FileInputStream(configLocation);
                conf = new Properties();
                conf.load(stream);
            } catch(IOException e) {
               throw new RuntimeException("Configuration file problem with " + appConfigName + ". Reason: " +
                                           e.getMessage());
           }
        }
    }

    public static String getPeerList() {
    	String peerLocation = null;
        if (System.getProperty("org.tigr.htc.config.peerlocation") != null) {
            // The user has specified a custom peer list config.
            peerLocation = System.getProperty("org.tigr.htc.config.peerlocation");
        }
        else {
            // Use the standard config file location for the webapp
            try {
                URL peerListURL = HTCConfig.class.getClassLoader().getResource("conf/" + peerListName);

                if (peerListURL == null) {
                    String msg = "Peer list configuration file not found.";
                    log.error(msg);
                    throw new FileNotFoundException(msg);
                }

                peerLocation = peerListURL.getPath();
            } catch(IOException e) {
               throw new RuntimeException("Configuration file problem with " + peerListName + ". Reason: " +
                                           e.getMessage());
           }
        }
        return peerLocation;
    }
    
    /**
     * The method <code>setConfigLocation<code>
     *
     *
     * @param pLocation
     *
     * @throws IllegalArgumentException
     *
     */
    public static void setConfigLocation(String pLocation) throws IllegalArgumentException {
        if (pLocation != null) {
            log.info("Changing config file location to " + pLocation);

            configLocation = pLocation;
        } else {
            log.fatal("null config file location was passed.");

            throw new IllegalArgumentException("Config Location passed was null");
        }
    }

    /**
     * The method <code>getConfigLocation<code>
     *
     *
     * @return
     *
     */
    public static String getConfigLocation() {
        return configLocation;
    }

    /**
     * The method <code>getConfig<code>
     *
     *
     * @return
     *
     * @throws RuntimeException
     *
     */
    public static Properties getConfig() throws RuntimeException {
        log.debug("In getConfig ");
        HTCConfig.initConfig();

        return conf;
    }

    /**
     * The method <code>setConfig<code>
     *
     *
     * @param pConf
     *
     */
    public static void setConfig(Properties pConf) {
        log.debug("In setConfig.");

        try {
            pConf.store(new FileOutputStream(configLocation), "Server Config");
        }
        catch(FileNotFoundException e) {
            log.error("FileNotFoundException: missing property file " + e.getMessage());
        }
        catch(IOException e) {
            log.error("IOexception missing property file " + e.getMessage());
        }
    }

    /**
     * The method <code>propertyNames<code>
     *
     * @return
     */
    public static Enumeration propertyNames() {
        log.debug("In propertyNames.");
        HTCConfig.initConfig();

        return conf.propertyNames();
    }

    /**
     * The method <code>getProperty<code>
     *
     * @param pKey
     *
     * @return
     *
     * @throws RuntimeException
     *
     */
    public static String getProperty(String pKey) throws RuntimeException {

        log.debug("In getProperty: " + pKey);
        HTCConfig.initConfig();

        String val = conf.getProperty(pKey);

        log.debug("Returning " + val);

        return val;
    }

    /**
     * The method <code>setProperty<code>
     *
     * @param pKey
     * @param pValue
     *
     * @throws RuntimeException
     *
     */
    public static void setProperty(String pKey, String pValue) throws RuntimeException {
        log.debug("In setProperty " + pKey + " " + pValue);
        HTCConfig.initConfig();
        conf.setProperty(pKey, pValue);
        HTCConfig.setConfig(conf);
        HTCConfig.fireConfigUpdate();
    }

    /**
     * The method <code>getProperty<code>
     *
     * @param pKey
     * @param pDefault
     *
     * @return
     *
     * @throws RuntimeException
     *
     */
    public static String getProperty(String pKey, String pDefault) throws RuntimeException {
        log.debug("In getProperty " + pKey + " " + pDefault);
        HTCConfig.initConfig();

        return conf.getProperty(pKey, pDefault);
    }

    // 
    // Event Listener methods
    // 

    /**
     * The method <code>addConfigUpdateListener<code>
     *
     * @param pListener
     *
     */
    public static void addConfigUpdateListener(ConfigUpdateLI pListener) {
        list.add(ConfigUpdateLI.class, pListener);
    }

    /**
     * The method <code>removeConfigUpdateListener<code>
     * 
     * @param pListener
     *
     */
    public static void removeConfigUpdateListener(ConfigUpdateLI pListener) {
        list.remove(ConfigUpdateLI.class, pListener);
    }

    /**
     * The method <code>fireConfigUpdate<code>
     */
    private static void fireConfigUpdate() {
	log.info("Fire a config update.");

        EventListener[] listeners = list.getListeners(ConfigUpdateLI.class);

        // Process the listeners last to first, notifying
        if (listeners != null) {
            for (int i = listeners.length - 1; i >= 0; i--) {
                ((ConfigUpdateLI) listeners[i]).configUpdated();
            }
        }
    }
}
